﻿/*******************************************************************************

    Units of Measure for C# applications

    Copyright (C) 2013 Marek Aniola

    This program is provided to you under the terms of the license
    as published at http://unitsofmeasure.codeplex.com/license


********************************************************************************/
using System;
using System.Globalization;
using System.Collections.Generic;
using System.Linq;

namespace Man.UnitsOfMeasure
{
	public abstract class UnitSystem<T> where T : struct
	{
		#region Fields
		private Dictionary<string, UnitOfMeasure<T>> m_glossary;
		#endregion

		#region Properties

		private NumberStyles m_valueStyle;
		public NumberStyles ValueStyle { get { return m_valueStyle; } set { m_valueStyle = value; } }

		private ITextParser<T> m_parser;
		public ITextParser<T> Parser { get { return m_parser; } set { m_parser = value; } }

		#endregion

		#region Constructor(s)
		protected UnitSystem(int capacity, StringComparison lookup)
		{
			IEqualityComparer<string> comparer;

			if (lookup == StringComparison.CurrentCulture) comparer = StringComparer.CurrentCulture;
			else if (lookup == StringComparison.CurrentCultureIgnoreCase) comparer = StringComparer.CurrentCultureIgnoreCase;
			else if (lookup == StringComparison.InvariantCulture) comparer = StringComparer.InvariantCulture;
			else if (lookup == StringComparison.InvariantCultureIgnoreCase) comparer = StringComparer.InvariantCultureIgnoreCase;
			else if (lookup == StringComparison.Ordinal) comparer = StringComparer.Ordinal;
			else comparer = StringComparer.OrdinalIgnoreCase;

			m_glossary = new Dictionary<string, UnitOfMeasure<T>>(capacity, comparer);
			m_valueStyle = NumberStyles.Number;
			m_parser = new TextParser<T>();
		}
		#endregion

		#region Units contructors

		public UnitOfMeasure<T> Unit(Measure<T> measure, string separator, string[] symbol)
		{
			var unit = new UnitOfMeasure<T>(measure, separator, symbol);
			if (symbol != null)
			{
				foreach (string s in symbol) m_glossary.Add(s, unit);
			}
			return unit;
		}

		public UnitOfMeasure<T> Unit(Measure<T> measure, params string[] symbol)
		{
			return Unit(measure, " ", symbol);
		}

		public UnitOfMeasure<T> Unit(Magnitude magnitude, string separator, string[] symbol)
		{
			return Unit(new Measure<T>(magnitude), separator, symbol);
		}

		public UnitOfMeasure<T> Unit(Magnitude magnitude, params string[] symbol)
		{
			return Unit(new Measure<T>(magnitude), " ", symbol);
		}

		#endregion

		#region Indexer(s)

		public UnitOfMeasure<T> this[string symbol]
		{
			get 
			{
				UnitOfMeasure<T> unit;
				return (m_glossary.TryGetValue(symbol, out unit) ? unit : null);
			}
		}

		#endregion

		#region IEnumerable(s)

		public IEnumerable<UnitOfMeasure<T>> Units(Magnitude magnitude)
		{
			return Units(new Dimension(magnitude));
		}

		public IEnumerable<UnitOfMeasure<T>> Units(Dimension dimension)
		{
			return (from kv in m_glossary where kv.Value.Sense == dimension select kv.Value).Distinct();
		}

		public IEnumerable<string> Symbols(Magnitude magnitude)
		{
			return Symbols(new Dimension(magnitude));
		}

		public IEnumerable<string> Symbols(Dimension dimension)
		{
			foreach (var unit in Units(dimension))
			{
				for (int i = 0; i < unit.SymbolCount; i++) yield return unit.Symbol(i);
			}
		}

		#endregion

		#region Parser

		public bool TryParse(string input, IFormatProvider fp, out Quantity<T> result)
		{
			return m_parser.TryParse(input, this, m_valueStyle, fp, out result);
		}

		public bool TryParse(string input, out Quantity<T> result)
		{
			return m_parser.TryParse(input, this, m_valueStyle, CultureInfo.CurrentCulture, out result);
		}

		public bool TryParse(string input, IFormatProvider fp, out Level<T> result)
		{
			return m_parser.TryParse(input, this, m_valueStyle, fp, out result);
		}

		public bool TryParse(string input, out Level<T> result)
		{
			return m_parser.TryParse(input, this, m_valueStyle, CultureInfo.CurrentCulture, out result);
		}

		#endregion
	}
}
